// 导入 Web3.js 库
import Web3 from "web3";
import {
  showSuccessToast,
  showLoadingToast,
  showFailToast,
  closeToast,
} from "vant";
let web3 = null;

/**
 * 连接钱包并返回已连接的 Web3 实例
 * 如果用户未连接钱包，将会弹出钱包连接请求
 * @returns {Promise<Web3>} 已连接的 Web3 实例
 * @throws {Error} 如果用户拒绝连接钱包或找不到以太坊提供者
 */
async function connectWallet() {
  return new Promise(async (resolve, reject) => {
    try {
      if (window.ethereum) {
        const accounts = await window.ethereum.request({
          method: "eth_requestAccounts",
        });
        web3 = await new Web3(window.ethereum); // 将连接的 Web3 实例赋值给模块级别的 web3 变量
        let address = accounts[0];
        resolve(address);
        return address;
      } else {
        reject();
        console.error("无法找到以太坊提供商。请确认钱包已安装并登录");
      }
    } catch (error) {
      reject(error);
      console.error("连接钱包失败");
    }
  });
}

/**
 * 创建合约对象
 * @param {object} contractData - 合约数据对象，包含ABI和合约地址
 * @param {string} abi
 * @param {string} address
 * @param {string} gasPrice gas费
 * @returns {object} 合约对象
 * @throws {Error} 如果无法创建合约对象
 */
async function createContract(contractData) {
  return new Promise(async (resolve, reject) => {
    try {
      if (!web3) {
        await connectWallet();
        await window.ethereum.request({
          method: "eth_requestAccounts",
        });
        web3 = await new Web3(window.ethereum);
      }
      let { abi, address } = contractData;
      if (typeof abi === "string") {
        abi = JSON.parse(abi);
      }
      if (!abi || !address) {
        console.error(
          "createContract: 创建合约对象需要提供正确ABI和地址:",
          contractData
        );
        reject(
          "createContract: 创建合约对象需要提供正确ABI和地址:",
          contractData
        );
      }
      const gasPrice = await web3.eth.getGasPrice()
      let gas = gasPrice * 1.5
      gas = Math.round(gas)
      // console.log("gasPrice>>>",gasPrice)
      resolve(new web3.eth.Contract(abi, address, { gasPrice: gas + '' }));
      // resolve(new web3.eth.Contract(abi, address));

    } catch (error) {

      reject(error);
    }
  });
}

/**
 * 验证钱包地址是否正确
 * @param {string} address - 要验证的钱包地址 需要传之前转字符串
 * @returns {boolean} 是否是正确的钱包地址
 */
async function validateAddress(address) {
  // if (!web3.utils.isAddress(address)) {
  //   console.error("钱包格式错误:", address);
  //   return(false); // 钱包地址格式不正确
  // }else{
  // }
  // resolve(true);
  return await web3.utils.isAddress(address);
}

/**
 * 转账代币
 * @param {object} transferData - 包含转账所需的信息对象
 * @param {object} contractAddress - 代币合约地址
 * @param {string} recipientAddress - 接收代币的地址
 * @param {string} amount - 转账的金额
 * @param {string} senderAddress - 发送代币的地址，默认为当前连接的钱包地址
 * @returns {Promise<string>} 交易哈希
 */
function transferToken(
  contractAddress,
  recipientAddress,
  amount,
  decimals,
  senderAddress
) {
  return new Promise(async (resolve, reject) => {
    try {
      if (!web3) {
        web3 = await new Web3(window.ethereum);
      }
      console.log(contractAddress,
        recipientAddress,
        amount,
        decimals,
        senderAddress);
      await validateAddress(recipientAddress);
      const contract = await createContract(contractAddress);
      const decimal = decimals || (await contract.methods.decimals().call()).toString();
      let quantity = amount;
      if (decimal && decimal > 6) {
        quantity = quantity * 1000000;
        for (let i = 0; i < decimal - 6; i++) {
          quantity += "0";
        }
      } else {
        let sb = 1;
        for (let i = 0; i < (decimal || 0); i++) {
          sb += "0";
        }
        quantity = quantity * sb;
      }
      const transfer = await contract.methods.transfer(recipientAddress, quantity);//币安支付方法
      // const transfer =await contract.methods.transferProxyOwnership(recipientAddress);//马蹄支付方法
      if (!senderAddress) {
        const accounts = await web3.eth.getAccounts();
        senderAddress = accounts[0]; // 使用连接钱包的第一个地址
      }
      const transaction = await transfer.send({
        from: senderAddress,
      });
      resolve(transaction.transactionHash);
    } catch (error) {
      console.error("🚀  transferToken  转账代币错误:", error);
      reject(error);
    }
  });
}

/**
 * NFT转让
 *
 * @param {string} contractAddress - NFT合约地址
 * @param {string} recipientAddress - 接收NFT的地址
 * @param {string} tokenId - 要转让的NFT的ID
 * @param {string} senderAddress - 发送NFT的地址，默认为当前连接的钱包地址
 * @returns {Promise<string>} 交易哈希
 */
function transferNFT(
  contractAddress,
  recipientAddress,
  tokenId,
  senderAddress
) {
  return new Promise(async (resolve, reject) => {
    try {
      await validateAddress(recipientAddress);
      const contract = await createContract(contractAddress);
      if (!senderAddress) {
        const accounts = await web3.eth.getAccounts();
        senderAddress = accounts[0]; // 使用连接钱包的第一个地址
      }
      await validateAddress(senderAddress);
      const safeTransfer = contract.methods.safeTransferFrom(
        senderAddress,
        recipientAddress,
        tokenId
      );
      const transaction = await safeTransfer.send({
        from: senderAddress,
      });
      resolve(transaction.transactionHash);
    } catch (error) {
      console.error("🚀  transferNFT  NFT转账错误:", error);
      reject(error);
    }
  });
}

/**
 * 查询代币余额
 * @param {object} contractAddress - 代币合约地址
 * @param {string} address - 查询余额的地址
 * @returns {Promise<string>} 指定地址的代币余额
 */
function getBalance(contractAddress, address) {
  return new Promise(async (resolve, reject) => {
    try {
      const contract = await createContract(contractAddress);
      const decimal = (await contract.methods.decimals().call()).toString();
      if (!address) {
        const accounts = await web3.eth.getAccounts();
        address = accounts[0]; // 使用连接钱包的第一个地址
      }
      const balanceNumber = await contract.methods.balanceOf(address).call(); // 调用合约的balanceOf方法查询余额
      const balance = balanceNumber.toString() / Math.pow(10, decimal);
      resolve(balance);
    } catch (error) {
      console.error("🚀  getBalance  查询代币余额错误:", error);
      reject(error);
    }
  });
}

/**
 * 切换网络
 * @param {object} networkData - 包含网络切换所需的信息对象
 * @param {number} networkData.chainId - 要切换的网络 ID
 * @param {string} networkData.chainName - 链的名称
 * @param {array[]} networkData.rpcUrls - 链的 RPC URL 列表
 * @param {array[]} networkData.blockExplorerUrls - 链的区块浏览器 URL 列表
 * @param {object} networkData.nativeCurrency - 链的本地货币信息
 * @returns {Promise<boolean>} 网络切换成功的消息
 * @throws {Error} 如果切换网络失败
 */
function switchNetwork(networkData) {
  return new Promise(async (resolve, reject) => {
    try {
      await connectWallet();
      const currentNetworkId = await ethereum.request({
        method: "eth_chainId",
      });
      const { chainId } = networkData;
      if (currentNetworkId !== chainId) {
        await ethereum.request({
          method: "wallet_addEthereumChain",
          // method: 'wallet_switchEthereumChain',
          params: [networkData],
        });
      }
      if (currentNetworkId) resolve(true);
    } catch (error) {
      console.error("🚀  switchNetwork  网络切换错误:", error);
      reject(error);
    }
  });
}

/**
 * 授权代币权限
 * @param {object} contractAddress - 代币合约地址
 * @param {string} recipient - 接收代币的地址(授权给谁的地址!!)
 * @param {string} amount - 授权的金额
 * @param {string} ownerAddress - 代币所有者的地址（可选，默认为当前连接钱包的地址）
 * @returns {Promise<string>} 成功授权返回余额
 */
function approveToken(contractAddress, recipient, amount, ownerAddress) {
  return new Promise(async (resolve, reject) => {
    try {
      await validateAddress(recipient);
      const contract = await createContract(contractAddress);
      const decimal = (await contract.methods.decimals().call()).toString();

      let quantity = amount;
      if (decimal && decimal > 6) {
        quantity = quantity * 1000000;
        for (let i = 0; i < decimal - 6; i++) {
          quantity += "0";
        }
      } else {
        let sb = 1;
        for (let i = 0; i < (decimal || 0); i++) {
          sb += "0";
        }
        quantity = quantity * sb;
      }
      if (!ownerAddress) {
        const accounts = await web3.eth.getAccounts();
        ownerAddress = accounts[0];
      }
      await validateAddress(ownerAddress);
      const result = await contract.methods.approve(recipient, quantity).send({
        from: ownerAddress,
      });
      resolve(result.status.toString()); // 成功时解决Promise并返回授权余额
    } catch (error) {
      console.error("🚀  approveToken  授权代币权限失败:", error);
      reject(error);
    }
  });
}

/**
 * 获取授权代币金额
 * @param {string} contractAddress - 代币合约地址
 * @param {string} spenderAddress - 授权代币的钱包地址(授权给谁的地址!!)
 * @param {string} myAddress - 代币所有者的地址（默认为当前连接钱包的地址）
 * @return  {Promise<string>} 授权金币字符串
 * @throws {Error} 如果授权失败
 */
function getAllowance(contractAddress, spenderAddress, myAddress) {
  return new Promise(async (resolve, reject) => {
    try {
      if (!web3) {
        await connectWallet();
      }
      if (!myAddress) {
        const accounts = await web3.eth.getAccounts();
        myAddress = accounts[0]; // 使用连接钱包的第一个地址
      }
      const contract = await createContract(contractAddress);
      const decimal = (await contract.methods.decimals().call()).toString();

      const allowance = await contract.methods
        .allowance(myAddress, spenderAddress)
        .call();
      resolve(allowance.toString() / Math.pow(10, decimal));
    } catch (error) {
      console.error("🚀  getAllowance  获取授权代币金额错误:", error);
      reject(error);
    }
  });
}

/**
 * 判断用户是否具有授权签名，并执行授权签名
 * @param {string} message - 授权签名的消息
 */
function authorizeSignature(message) {
  return new Promise(async (resolve, reject) => {
    try {
      await connectWallet();
      const accounts = await web3.eth.getAccounts();
      const signature = await web3.eth.personal.sign(message, accounts[0], "");
      console.log("授权签名成功:", signature);
      resolve(true);
    } catch (error) {
      console.error("🚀 authorizeSignature 授权签名发生错误:", error);
      reject(error);
    }
  });
}

/**
 * 自定义方法sendAmount
 * @param {string} contractAddress - 代币合约地址
 * @param {string} sendAddress - 发送给谁的地址
 * @param {array} addressArray - 数组地址 创建订单返回的
 * @param {array} amountArray - 数组价格 创建订单返回的
 * @param {string} myAddress - 我的地址
 * @throws {Error} 如果授权失败
 */
function customSend(
  contractAddress,
  sendAddress,
  addressArray,
  amountArray,
  myAddress
) {
  return new Promise(async (resolve, reject) => {
    try {
      const contract = await createContract(contractAddress);
      let data = await contract.methods
        .sendAmount(sendAddress, addressArray, amountArray)
        .send({
          from: myAddress,
        });
      resolve(data);
    } catch (error) {
      reject(error);
      console.error("🚀  returnnewPromise :", error);
    }
  });
}

/**
 * 自定义方法payment
 * @param {string} contractAddress - 代币合约地址
 * @param {string} orderId - 订单号
 * @param {string} sendAddress - 发送给谁的地址 收款地址
 * @param {array} addressArray - 数组地址 创建订单返回的
 * @param {array} amountArray - 数组价格 创建订单返回的
 * @param {string} myAddress - 我的地址
 * @throws {Error} 如果授权失败
 */
function payment(
  contractAddress,
  orderId,
  sendAddress,
  addressArray,
  amountArray,
  myAddress
) {
  return new Promise(async (resolve, reject) => {
    try {
      const contract = await createContract(contractAddress);
      if (!myAddress) {
        const accounts = await web3.eth.getAccounts();
        myAddress = accounts[0]; // 使用连接钱包的第一个地址
      }

      let data = await contract.methods
        .payment(orderId, sendAddress, addressArray, amountArray)
        .send({
          from: myAddress,
        });
      resolve(data);
    } catch (error) {
      reject(error);
      console.error("🚀  returnnewPromise :", error);
    }
  });
}

/**
 * 自定义方法fcbSwap
 * @param {string} contractAddress - 代币合约地址
 * @param {string} amount - 金额
 * @param {string} myAddress - 我的地址
 * @throws {Error} 如果授权失败
 */
function fcbSwap(contractAddress, amount, myAddress) {
  return new Promise(async (resolve, reject) => {
    try {
      const contract = await createContract(contractAddress);
      if (!myAddress) {
        const accounts = await web3.eth.getAccounts();
        myAddress = accounts[0]; // 使用连接钱包的第一个地址
      }
      amount = amount.toString() * Math.pow(10, "18");
      let data = await contract.methods.fcbSwap(amount.toString()).send({
        from: myAddress,
      });
      resolve(data);
    } catch (error) {
      reject(error);
      console.error("🚀  returnnewPromise :", error);
    }
  });
}

export {
  //链接钱包
  connectWallet,
  //
  getBalance,
  transferToken,
  validateAddress,
  switchNetwork,
  createContract,
  approveToken,
  transferNFT,
  getAllowance,
  authorizeSignature,
  customSend,
  payment,
  fcbSwap,
};
